package com.libui.webview.services

import android.util.Base64
import com.libbase.cast
import com.libbase.castList
import com.libbase.utils.JsonUtils
import com.libnetwork.HttpException
import com.libnetwork.extension.absoluteApiUrl
import com.libnetwork.httpservice.HttpService
import io.reactivex.rxjava3.core.Observable
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Headers.Companion.toHeaders
import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import java.io.IOException

class WebViewService {
    companion object {
        private val JSON: MediaType = "application/json; charset=utf-8".toMediaType()

        val instance: WebViewService by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
            WebViewService()
        }
    }

    fun request(
        method: String?, baseUrl: String?, url: String,
        headers: Map<String, String>?,
        params: Map<String, Any>?,
        data: Any?,
    ): Observable<String> {
        val observable = Observable.create { emitter ->
            val requestUrl: String = if (baseUrl != null) {
                if (baseUrl.endsWith("/")) baseUrl + url else "$baseUrl/$url"
            } else {
                url
            }
            val httpUrlBuilder: HttpUrl.Builder? =
                requestUrl.absoluteApiUrl.toHttpUrlOrNull()?.newBuilder()

            if (httpUrlBuilder == null) {
                emitter.onError(IllegalArgumentException("Parse HttpUrl error! url: $requestUrl"))
                return@create
            }

            params?.let {
                it.forEach { entry ->
                    when (val value: Any = entry.value) {
                        is String -> {
                            httpUrlBuilder.addQueryParameter(entry.key, value)
                        }

                        is Int -> {
                            httpUrlBuilder.addQueryParameter(entry.key, value.toString())
                        }

                        is Boolean -> {
                            httpUrlBuilder.addQueryParameter(entry.key, value.toString())
                        }
                    }
                }
            }

            val requestBuilder: Request.Builder = Request.Builder().url(httpUrlBuilder.build())
            if (headers != null) {
                requestBuilder.headers(headers.toHeaders())
            }

            data?.let {
                castList<Map<String, Any>>(it)?.let { dataList ->
                    requestBuilder.method(
                        method?.uppercase() ?: "POST", formData(dataList)
                    )
                } ?: run {
                    val dataMap: Map<String, Any>? = cast(it)
                    if (dataMap != null && dataMap.any { entry ->
                            val value: Map<String, Any> = cast(entry.value) ?: return@any false
                            val type: String = value["type"] as? String ?: return@any false
                            return@any arrayOf("file", "image").contains(type)
                        }) {
                        requestBuilder.method(
                            method?.uppercase() ?: "GET",
                            formData(
                                dataMap.map { entry ->
                                    mapOf(
                                        "key" to entry.key, "value" to entry.value
                                    )
                                },
                            )
                        )
                    } else {
                        val requestBody = JsonUtils.json.toJson(data).toRequestBody(JSON)
                        requestBuilder.method(method?.uppercase() ?: "POST", requestBody)
                    }
                }
            } ?: run {
                requestBuilder.method(method?.uppercase() ?: "POST", null)
            }

            val call: Call = HttpService.instance.okHttpClient.newCall(requestBuilder.build())
            call.enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    emitter.onError(e)
                }

                override fun onResponse(call: Call, response: Response) {
                    if (response.code != 200) {
                        emitter.onError(HttpException(response))
                        return
                    }

                    emitter.onNext(response.body?.string() ?: "")
                    emitter.onComplete()
                }
            })

            emitter.setCancellable {
                call.cancel()
            }
        }

        return HttpService.instance.retry(observable)
    }

    private fun formData(data: List<Map<String, Any>>): MultipartBody {
        val multipartBodyBuilder = MultipartBody.Builder()
        data.forEach { element ->
            val key: String = cast(element["key"]) ?: return@forEach
            val value: Any = cast(element["value"]) ?: return@forEach

            if (value is String) {
                multipartBodyBuilder.addFormDataPart(key, value)
            } else if (value is Map<*, *>) {
                val base64Str: String = value["data"] as? String ?: return@forEach

                val fileName: String? = value["fileName"] as? String
                val mediaType: MediaType? = (value["mimeType"] as? String)?.toMediaTypeOrNull()
                multipartBodyBuilder.addFormDataPart(
                    key, fileName, Base64.decode(base64Str, Base64.DEFAULT).toRequestBody(
                        mediaType
                    )
                )
            }
        }

        return multipartBodyBuilder.build()
    }
}



